from mastergoal.common.state import State
from mastergoal.common.square import Square
from mastergoal.common.team import Team
from mastergoal.common.board import Board

class CommonRules:
    """ Base class for Rules """
    (
        INVALID_MAX_KICKS,
        
        INVALID_MUST_REACH_BALL, # must reach ball in the first play of after goal
        INVALID_MUST_MOVE_BALL, # player must move ball if it is around it
        INVALID_PLAYER_MOVE, # basic rule violated
        INVALID_NEUTRAL_IN_AREA, # neutral square in area, player must reach ball
        INVALID_PLAYER_CORNER, #(no posicionarse en su propio corner)
        
        # Valid player movements
        VALID_OK, #(termino movimiento jugador y pelota, se usa para pelota tambien)
        VALID_REACHED_BALL, # (termino movimiento jugador, alcanzo la pelota)
       
        # Invalid ball movements
        INVALID_BALL_NEUTRAL, #(pelota en casilla neutra, no se puede mover)
        INVALID_MOVE, #(violo una regla basica de pelota)
        INVALID_AUTOPASS, #(quiso hacer un autopase)
        INVALID_PASS_OPPONENT, #(le quiso pasar al contrario)
        INVALID_BALL_CORNER, #(quiso enviar la pelota a su propio corner)
        INVALID_LEAVE_BALL, #(no puede dejar la pelota en su area en la primera jugada y despues de un gol)
        INVALID_FREE_BALL, #la pelota esta libre, no se puede mover
        INVALID_AUTOGOAL, #gol en contra
        INVALID_LITTLE_AREA_DEFENSE, #no se puede pasar la pelota sobre los defensores en el area chica
        INVALID_OVER_KEEPER, #no se puede pasar la pelota sobre los brazos del arquero en el area chica ni sobre su cuerpo
                            #en el area grande, no se puede sobre el cuerpo

        # Valid ball movements
        VALID_NEUTRAL, #(dejo pelota en casilla neutra)
        VALID_NEUTRAL_REPEAT,
        VALID_PASS, # (paso la pelota a otro de su equipo)
        VALID_REPEAT_TURN, #(llego a casilla especial.. otro turno)
        VALID_GOAL, #(metio gol)
        VALID_GOAL_RIGHT,
        VALID_GOAL_LEFT
        
    ) = range(25)
    
    MAX_KICKS = 3
    
    desc_rules = {INVALID_MAX_KICKS : "INVALID_MAX_KICKS", INVALID_MUST_REACH_BALL : "INVALID_MUST_REACH_BALL",
                 INVALID_MUST_MOVE_BALL : "INVALID_MUST_MOVE_BALL", INVALID_PLAYER_MOVE : "INVALID_PLAYER_MOVE" , 
                 INVALID_NEUTRAL_IN_AREA : "INVALID_NEUTRAL_IN_AREA" , INVALID_PLAYER_CORNER : "INVALID_PLAYER_CORNER", 
                 VALID_OK : "VALID_OK", VALID_REACHED_BALL : "VALID_REACHED_BALL", INVALID_BALL_NEUTRAL : "INVALID_BALL_NEUTRAL", 
                 INVALID_MOVE : "INVALID_MOVE", INVALID_AUTOPASS : "INVALID_AUTOPASS", INVALID_PASS_OPPONENT : "INVALID_PASS_OPPONENT",
                 INVALID_BALL_CORNER : "INVALID_BALL_CORNER", INVALID_LEAVE_BALL : "INVALID_LEAVE_BALL", INVALID_FREE_BALL : "INVALID_FREE_BALL", 
                 INVALID_AUTOGOAL : "INVALID_AUTOGOAL", INVALID_LITTLE_AREA_DEFENSE : "INVALID_LITTLE_AREA_DEFENSE", INVALID_OVER_KEEPER : "INVALID_OVER_KEEPER",
                 INVALID_OVER_KEEPER : "INVALID_OVER_KEEPER", VALID_NEUTRAL : "VALID_NEUTRAL", VALID_NEUTRAL_REPEAT : "VALID_NEUTRAL_REPEAT",
                 VALID_PASS : "VALID_PASS", VALID_REPEAT_TURN : "VALID_REPEAT_TURN", VALID_GOAL : "VALID_GOAL", 
                 VALID_GOAL_RIGHT : "VALID_GOAL_RIGHT", VALID_GOAL_LEFT : "VALID_GOAL_RIGHT", None : "NONE"}
    
    
    @staticmethod
    def ball_around(state, square):
        """ Check if ball is around square """
        ball = state.board.ballpiece
        return CommonRules.around(ball.square, square)
    
    
    @staticmethod
    def around(squareA, squareB):
        """ Check if squareA is around squareB """
        if squareB.col == 0:    
            return squareA.col <= squareB.col + 1 and squareA.row <= squareB.row + 1 and squareA.row >= squareB.row - 1
        else:    
            return squareA.col <= squareB.col + 1 and squareA.col >= squareB.col - 1 and squareA.row <= squareB.row + 1 and squareA.row >= squareB.row - 1
    
    
    @staticmethod
    def occupied(state, row, col):
        """ Checks if square is occupied by a piece """
        
        square = Square(row, col)
        
        # Check if ball is in square
        ball = state.board.ballpiece
        if ball.square == square:
            return True
        
        # Check if any players are in square
        for k,v in state.board.teams.iteritems():
            for player in v.players:
                if player.square == square:
                    return True
        
        return False
    
    @staticmethod
    def valid_player_move(state, move):
        """ Verifies if move is valid in the state context """
        
        # In the first ply or after a goal the player must reach the ball
        if state.first_ply and not CommonRules.ball_around(state, move.square):
            return (False, CommonRules.INVALID_MUST_REACH_BALL)
        
        # Checks player basic moves according to level
        # if not cls.correct_player_move(state, move):
        if not CommonRules.correct_player_move(state, move):
            return (False, CommonRules.INVALID_PLAYER_MOVE) 
        
        # Player can't move if it is around ball
        if CommonRules.ball_around(state, move.piece.square):
            return (False, CommonRules.INVALID_MUST_MOVE_BALL)
        
        # Checks if player reached the ball
        if CommonRules.ball_around(state, move.square):
            state.board.player_with_ball = move.piece
            return (True, CommonRules.VALID_REACHED_BALL)
        
        return (True, CommonRules.VALID_OK)
        
        
    @staticmethod
    def correct_player_move(state, move):
        """ Check player basic moves """
        
        col_before = move.piece.square.col
        col_after = move.square.col
        row_before = move.piece.square.row
        row_after = move.square.row
          
        # 1. Player can move at most two squares
        if abs(col_before-col_after) > 2 or abs(row_before - row_after) > 2:
            return False
        
        # 2. Player can't jump over other pieces
        # If player moves up (right) or down (left)
        if col_before == col_after:
            # If player moves down
            if row_after > row_before and abs(row_after - row_before) != 1:
                # Check if there is a piece in an intermediate square
                if CommonRules.occupied(state, row_after-1, col_before): 
                    return False
            # if player moves up
            elif row_before > row_after and abs(row_after - row_before) != 1:
                if CommonRules.occupied(state, row_after + 1, col_before):
                    return False
                
        # if player moves left (down) or right (up)
        elif row_before == row_after:
            # if player moves left
            if col_after > col_before and abs(col_after - col_before) != 1:
                if CommonRules.occupied(state, row_before, col_after-1):
                    return False
            elif col_before > col_after and abs(col_after - col_before) != 1:
                if CommonRules.occupied(state, row_before, col_after + 1):
                    return False     
                
        # if player moves in a diagonal          
        else:
            
            # L movement not allowed
            if abs(row_before-row_after) != abs(col_before-col_after):
                return False
            
            
            # if player moves two squares, not one
            if abs(col_before - col_after) != 1:
                if col_before > col_after and row_before < row_after:
                    if CommonRules.occupied(state, row_after - 1, col_after + 1):
                        return False
                
                elif col_before > col_after and row_before > row_after:
                    if CommonRules.occupied(state, row_after + 1, col_after + 1):
                        
                        return False
                    
                elif col_before < col_after and row_before < row_after:
                    if CommonRules.occupied(state, row_after - 1, col_after - 1):
                        return False
                    
                elif col_before < col_after and row_before > row_after:
                    if CommonRules.occupied(state, row_after + 1, col_after - 1):
                        return False
       
        # Player can't move to a square occupied by a piece
        if CommonRules.occupied(state, row_after, col_after):
            return False
        
        # Check board bounds
        if col_after < 0 or col_after > 10 or row_after < 1 or row_after > 13:   
            return False
        
        return True
        
    
    @staticmethod
    def valid_ball_move(state, move):
        """ Check ball basic moves """
        
        ball = state.board.ballpiece 
        col_before = move.piece.square.col
        row_before = move.piece.square.row
        col_after = move.square.col
        row_after = move.square.row
        
        # Ball can be moved up to four squares
        #if state.kicks >= 3:
        #    return (False, CommonRules.INVALID_MAX_KICKS)
    
        # Ball can't move without player around
        if move.player_moving is None:
            return (False, CommonRules.INVALID_FREE_BALL)
        
        # Ball can be moved up to four squares
        if abs(col_before-col_after) > 4 or abs(row_before-row_after) > 4:
            return (False, CommonRules.INVALID_MOVE)
        
        # L movement not valid
        if col_before != col_after and row_before != row_after:
            if abs(col_before - col_after) != abs(row_before - row_after):
                return (False, CommonRules.INVALID_MOVE)
        
        # Ball can't be moved to an occupied square
        if CommonRules.occupied(state, row_after, col_after):
            return (False, CommonRules.INVALID_MOVE)
        
        # Check board bounds
        if col_after < 0 or col_after > 10 or row_after < 0 or row_after > 14:
            return (False, CommonRules.INVALID_MOVE)
        
        if (col_after < 3 or col_after > 7) and (row_after == 0 or row_after == 14):
            return (False, CommonRules.INVALID_MOVE)
        
        if CommonRules.autopass(state.board, move.square, move.player_moving):
            return (False, CommonRules.INVALID_AUTOPASS)
        
        # Player can't move ball to its corner
        if Board.own_corner(move.player_moving.team.orientation, move.square.row, move.square.col):
            return (False, CommonRules.INVALID_BALL_CORNER)
        
        # Player can't pass ball to opponent
        majority = CommonRules.majority(state, move.square)
        if majority != move.player_moving.team.orientation \
            and majority != None \
            and not Board.is_goal(move.player_moving.team.orientation, move.square.row, move.square.col):
            return (False, CommonRules.INVALID_PASS_OPPONENT)
        
        # Player can't move ball to its big area
        if Board.big_area(move.player_moving.team.orientation, move.square.row, move.square.col) \
            and majority != move.player_moving.team.orientation:
            return (False, CommonRules.INVALID_LEAVE_BALL)
        
        if Board.is_goal(move.player_moving.team.orientation, move.square.row, move.square.col):
            return (True, CommonRules.VALID_GOAL)
        
        # Returns majority so it doesn't need to be recalculated in higher levels
        if majority:
            return (True, majority)
        else:
            return (True, CommonRules.VALID_OK)
    
        
    @staticmethod
    def autopass(board, square, piece_playing):
        
        if board.is_goal(piece_playing.team.orientation, square.row, square.col):
            return False
        
        if not piece_playing.square.adjacent(square):
            return False
        
        players_around = CommonRules.players_around_square(board, square, piece_playing.team.orientation)
        for player in players_around:
            if player != piece_playing:
                return False
        
        return True
    
    @staticmethod
    def players_around_square(board, square, orientation):
        """ Returns list of players around square """
        
        players_around = []
        
        team = board[orientation]
        for player in team.players:
            if CommonRules.around(player.square, square):
                players_around.append(player)
            
        return players_around
    
    @staticmethod
    def majority(state, square):
        """ Checks whether there is majority of a team around ball """
        
        teams_around_ball = {Team.UP:0, Team.DOWN:0}
        
        for k,v in state.board.teams.iteritems():
            for player in v.players:
                if CommonRules.around(player.square, square):
                    teams_around_ball[player.team.orientation] += 1
        
        team_up = teams_around_ball[Team.UP]
        team_down = teams_around_ball[Team.DOWN]
        
        if team_up > team_down:
            return Team.UP
        elif team_down > team_up:
            return Team.DOWN
        
        return None
    
    
    @staticmethod
    def players_around_ball(state, ball_move):
        """ Return current player's team members around the ball """
        """ If there are no players around the ball, an empty list is returned """
        
        players = []
        cur_player = state.player_with_ball
        team = state.board[cur_player.team.orientation]
        
        for player in team.players:
            if player != cur_player and CommonRules.ball_around(state, player.square):
                players.append(player)
        
        return players
                
            